package com.lc.ibps.platform.rest.bpmn;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.collect.Maps;
import com.lc.ibps.api.base.model.PartyEntity;
import com.lc.ibps.api.base.model.User;
import com.lc.ibps.api.bo.model.IDataObject;
import com.lc.ibps.api.org.service.IPartyEntityService;
import com.lc.ibps.base.core.util.AppUtil;
import com.lc.ibps.base.core.util.BeanUtils;
import com.lc.ibps.base.core.util.JacksonUtil;
import com.lc.ibps.base.web.context.ContextUtil;
import com.lc.ibps.bpmn.api.constant.ActionType;
import com.lc.ibps.bpmn.api.constant.NodeType;
import com.lc.ibps.bpmn.api.constant.PrivilegeMode;
import com.lc.ibps.bpmn.api.constant.TaskActionType;
import com.lc.ibps.bpmn.api.define.IBpmDefineReader;
import com.lc.ibps.bpmn.api.identity.BpmIdentityExtractService;
import com.lc.ibps.bpmn.api.model.define.NodeAttributes;
import com.lc.ibps.bpmn.api.model.identity.BpmIdentity;
import com.lc.ibps.bpmn.api.model.inst.IBpmProcInst;
import com.lc.ibps.bpmn.api.model.node.IBpmNodeDefine;
import com.lc.ibps.bpmn.api.model.node.SignNodeDefine;
import com.lc.ibps.bpmn.api.nat.task.NatTaskService;
import com.lc.ibps.bpmn.api.service.BpmBoService;
import com.lc.ibps.bpmn.api.service.BpmIdentityService;
import com.lc.ibps.bpmn.api.service.BpmProcInstService;
import com.lc.ibps.bpmn.api.service.BpmTaskService;
import com.lc.ibps.bpmn.api.service.SignService;
import com.lc.ibps.bpmn.persistence.entity.BpmCommonStatmentPo;
import com.lc.ibps.bpmn.persistence.entity.BpmExecPo;
import com.lc.ibps.bpmn.persistence.entity.BpmTaskPo;
import com.lc.ibps.bpmn.repository.BpmExecRepository;
import com.lc.ibps.bpmn.repository.BpmTaskRepository;
import com.lc.ibps.bpmn.service.BpmCommonStatmentService;
import com.lc.ibps.bpmn.utils.BpmExecUtil;
import com.lc.ibps.bpmn.utils.BpmPermissionUtil;
import com.lc.ibps.bpmn.utils.PartyUtil;
import com.lc.ibps.cloud.entity.APIResult;
import com.lc.ibps.org.party.persistence.entity.PartyEntityPo;
import com.lc.ibps.web.controller.BaseApiController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import net.sf.json.JSONObject;

/**
 * 流程任务查询接口
 *
 * <pre>
 *  
 * 构建组：ibps-platform-appapi
 * 作者：zhongjh
 * 邮箱：zjh20140614@163.com
 * 日期：2018年8月13日-下午5:01:44
 * 版权：广州流辰信息技术有限公司版权所有
 * </pre>
 */
@RequestMapping(value = "/api/webapi/bpmTaskService")
@Api( value = "/bpmTaskService",tags = "流程管理-流程任务服务")
@RestController("bpmTaskBaseRest")
public class BpmTaskBaseController extends BaseApiController {

	private IPartyEntityService entityService;
	private BpmBoService bpmBoService;
	private BpmTaskService bpmTaskService;
	private BpmTaskRepository bpmTaskRepository;
	private BpmProcInstService bpmInstService;
	private IBpmDefineReader bpmDefineReader;
	private NatTaskService natTaskService;
	private SignService signService;
	private BpmIdentityService bpmIdentityService;
	private BpmIdentityExtractService bpmIdentityExtractService;
	private BpmCommonStatmentService bpmCommonStatmentService;
	private BpmExecRepository bpmExecRepository;

	public BpmTaskBaseController() {
		entityService = AppUtil.getBean(IPartyEntityService.class);
		bpmBoService = AppUtil.getBean(BpmBoService.class);
		bpmTaskService = AppUtil.getBean(BpmTaskService.class);
		bpmTaskRepository = AppUtil.getBean(BpmTaskRepository.class);
		bpmInstService = AppUtil.getBean(BpmProcInstService.class);
		bpmDefineReader = AppUtil.getBean(IBpmDefineReader.class);
		natTaskService = AppUtil.getBean(NatTaskService.class);
		signService = AppUtil.getBean(SignService.class);
		bpmIdentityService = AppUtil.getBean(BpmIdentityService.class);
		bpmIdentityExtractService = AppUtil.getBean(BpmIdentityExtractService.class);
		bpmCommonStatmentService = AppUtil.getBean(BpmCommonStatmentService.class);
		bpmExecRepository = AppUtil.getBean(BpmExecRepository.class);
	}

	/**
	 * 获取同意、反对相关信息
	 * 
	 * @param taskId
	 * @param actionName
	 * @return
	 */
	@ApiOperation(value = "获取操作信息", notes = "获取同意反对操作信息")
	@RequestMapping(value = "/toAgree", method = RequestMethod.GET)
	public APIResult<Map<String, Object>> toAgree(
			@RequestParam(name = "taskId", required = false) @ApiParam(name = "taskId", value = "任务ID", required = false) String taskId,
			@RequestParam(name = "actionName", required = false) @ApiParam(name = "taskId", value = "动作类型", required = false) String actionName) {
		APIResult<Map<String, Object>> result = new APIResult<>();
		try {
			BpmTaskPo task = (BpmTaskPo) bpmTaskService.getByTaskId(taskId);
			if (BeanUtils.isEmpty(task))
				throw new RuntimeException("该任务已经被不存在！");
			BpmPermissionUtil.validatePression(taskId, actionName, ContextUtil.getCurrentUserId());

			String defId = task.getProcDefId();
			String nodeId = task.getNodeId();

			Map<String, Object> params = new HashMap<>();
			params.put("taskId", taskId);
			params.put("actionName", actionName);

			IBpmNodeDefine taskNodeDef = bpmDefineReader.getNode(defId, nodeId);
			// 非弃权动作并且是会签任务
			if (!"abandon".equals(actionName) && taskNodeDef.getType().equals(NodeType.SIGNTASK)) {
				IBpmProcInst bpmProcessInstance = bpmInstService.getProcInst(task.getProcInstId());
				notAbandon(task, bpmProcessInstance, params, taskNodeDef);
			}

			NodeAttributes nodeAttributes = taskNodeDef.getLocalProperties();
			String jumpType = nodeAttributes != null ? nodeAttributes.getJumpType() : "common";
			boolean hidePath = nodeAttributes != null ? nodeAttributes.isHidePath() : false;
			boolean hideOpinion = nodeAttributes != null ? nodeAttributes.isHideOpinion() : false;

			if (TaskActionType.AGREE.getKey().equals(actionName)) {
				handlePath(task, defId, params, taskNodeDef, jumpType);
			}

			BpmCommonStatmentPo defaultCommonStatment = bpmCommonStatmentService.getDefault(actionName,
					ContextUtil.getCurrentUserId());
			params.put("defaultCommonStatment", defaultCommonStatment);
			params.put("jumpType", jumpType);
			params.put("hidePath", hidePath);
			params.put("hideOpinion", hideOpinion);
			result.setData(params);
			result.setMessage("获取数据成功");
		} catch (Exception e) {
			result = getExceptionResult(result, ERROR, "获取数据失败", e);
		}

		return result;

	}

	/**
	 * 驳回
	 * 
	 * @param taskId
	 * @return
	 */
	@ApiOperation(value = "获取操作信息", notes = "获取驳回操作信息")
	@RequestMapping(value="toReject", method=RequestMethod.GET)
	public APIResult<Map<String, Object>> toReject(
			@RequestParam(name = "taskId", required = true) @ApiParam(name = "taskId", value = "任务ID", required = true) String taskId) {
		APIResult<Map<String, Object>> result = new APIResult<>();
		try {
			BpmPermissionUtil.validatePression(taskId, ActionType.REJECT.getKey(), ContextUtil.getCurrentUserId());

			BpmTaskPo task = bpmTaskRepository.get(taskId);
			if (BeanUtils.isEmpty(task))
				throw new RuntimeException("该任务已经完成！");

			String procDefId = task.getProcDefId();
			String nodeId = task.getNodeId();

			// 查询历史轨迹
			BpmExecPo currExec = bpmExecRepository.getByTaskId(taskId);
			List<IBpmNodeDefine> hisBpmNodeDefs = BpmExecUtil.getHistoryListBpmNodeDefine(currExec, procDefId, nodeId,
					"pre");
			// 节点去重
			hisBpmNodeDefs = new ArrayList<IBpmNodeDefine>(new HashSet<IBpmNodeDefine>(hisBpmNodeDefs));
			// 过滤本节点
			for (Iterator<IBpmNodeDefine> iterator = hisBpmNodeDefs.iterator(); iterator.hasNext();) {
				IBpmNodeDefine iBpmNodeDefine = iterator.next();
				if (iBpmNodeDefine.getNodeId().equalsIgnoreCase(nodeId)) {
					iterator.remove();
				}
			}

			// 允许直来直往的节点
			List<IBpmNodeDefine> bpmExecUserNode = new ArrayList<IBpmNodeDefine>();
			// 允许按流程图执行的节点
			List<IBpmNodeDefine> bpmExecGoMapUserNode = new ArrayList<IBpmNodeDefine>();
			List<String> nodeids = new ArrayList<String>();
			String procInstId = task.getProcInstId();
			for (IBpmNodeDefine node : hisBpmNodeDefs) {
				if (node.getType().equals(NodeType.USERTASK) && !nodeids.contains(node.getNodeId())) {
					bpmExecUserNode.add(node);
					nodeids.add(node.getNodeId());
					boolean isHavePre = BpmExecUtil.hasAndOrGateway(procInstId, node.getNodeId(), "pre");
					boolean isHaveAfter = BpmExecUtil.hasAndOrGateway(procInstId, node.getNodeId(), "after");
					if (!(isHavePre && isHaveAfter)) {
						bpmExecGoMapUserNode.add(node);
					}
				}
			}

			BpmCommonStatmentPo defaultCommonStatment = bpmCommonStatmentService
					.getDefault(TaskActionType.REJECT.getKey(), ContextUtil.getCurrentUserId());
			Map<String, Object> data = Maps.newHashMap();
			data.put("taskId", taskId);
			data.put("bpmExecUserNode", bpmExecUserNode);
			data.put("bpmExecGoMapUserNode", bpmExecGoMapUserNode);
			data.put("defaultCommonStatment", defaultCommonStatment);
			result.setData(data);
			result.setMessage("获取任务信息成功");
		} catch (Exception e) {
			result = getExceptionResult(result, ERROR, "获取任务信息失败", e);
		}
		return result;
	}

	/**
	 * 驳回上一步
	 * 
	 * @param taskId
	 * @return
	 */
	@ApiOperation(value = "获取操作信息", notes = "获取驳回上一步操作信息")
	@RequestMapping(value="toRejectToPrevious", method=RequestMethod.GET)
	public APIResult<Map<String, Object>> toRejectToPrevious(
			@RequestParam(name = "taskId", required = true) @ApiParam(name = "taskId", value = "任务ID", required = true) String taskId) {
		APIResult<Map<String, Object>> result = new APIResult<>();
		try {
			BpmPermissionUtil.validatePression(taskId, ActionType.REJECT_TO_PREVIOUS.getKey(), ContextUtil.getCurrentUserId());

			BpmTaskPo task = bpmTaskRepository.get(taskId);
			if (BeanUtils.isEmpty(task))
				throw new RuntimeException("该任务已经完成！");

			BpmCommonStatmentPo defaultCommonStatment = bpmCommonStatmentService.getDefault(ActionType.REJECT.getKey(),
					ContextUtil.getCurrentUserId());
			Map<String, Object> data = Maps.newHashMap();
			data.put("taskId", taskId);
			data.put("defaultCommonStatment", defaultCommonStatment);
			result.setData(data);
			result.setMessage("获取任务信息成功");
		} catch (Exception e) {
			result = getExceptionResult(result, ERROR, "获取任务信息失败", e);
		}
		return result;
	}

	/**
	 * 驳回发起人
	 * 
	 * @param taskId
	 * @return
	 */
	@ApiOperation(value = "获取操作信息", notes = "获取驳回发起人操作信息")
	@RequestMapping(value="toRejectToStart", method=RequestMethod.GET)
	public APIResult<Map<String, Object>> toRejectToStart(
			@RequestParam(name = "taskId", required = true) @ApiParam(name = "taskId", value = "任务ID", required = true) String taskId) {
		APIResult<Map<String, Object>> result = new APIResult<>();
		try {
			BpmPermissionUtil.validatePression(taskId, ActionType.REJECT_TO_START.getKey(),  ContextUtil.getCurrentUserId());

			BpmTaskPo task = bpmTaskRepository.get(taskId);
			if (BeanUtils.isEmpty(task))
				throw new RuntimeException("该任务已经完成！");

			BpmCommonStatmentPo defaultCommonStatment = bpmCommonStatmentService
					.getDefault(ActionType.REJECT_TO_START.getKey(), ContextUtil.getCurrentUserId());
			Map<String, Object> data = Maps.newHashMap();
			data.put("taskId", taskId);
			data.put("defaultCommonStatment", defaultCommonStatment);
			result.setData(data);
			result.setMessage("获取任务信息成功");
		} catch (Exception e) {
			result = getExceptionResult(result, ERROR, "获取任务信息失败", e);
		}
		return result;
	}

	/**
	 * 跳转添加会签任务
	 * 
	 * @param taskId
	 * @return
	 */
	@ApiOperation(value = "获取操作信息", notes = "获取添加会签操作信息")
	@RequestMapping(value="toAddSignTask", method=RequestMethod.GET)
	public APIResult<Map<String, Object>> toAddSignTask(
			@RequestParam(name = "taskId", required = true) @ApiParam(name = "taskId", value = "任务ID", required = true) String taskId) {
		APIResult<Map<String, Object>> result = new APIResult<>();
		try {
			BpmPermissionUtil.validatePression(taskId, "addSign", ContextUtil.getCurrentUserId());
			Map<String, Object> data = Maps.newHashMap();
			data.put("taskId", taskId);
			result.setData(data);
			result.setMessage("获取任务信息成功");
		} catch (Exception e) {
			result = getExceptionResult(result, ERROR, "获取任务信息失败", e);
		}
		return result;
	}

	/**
	 * 非弃权动作并且是会签任务
	 *
	 * @param task
	 * @param bpmProcessInstance
	 * @param autoView
	 * @param taskNodeDef
	 */
	private void notAbandon(BpmTaskPo task, IBpmProcInst bpmProcessInstance, Map<String, Object> autoView,
			IBpmNodeDefine taskNodeDef) {
		IDataObject dataObjects = bpmBoService.getDataByInst(bpmProcessInstance);
		Map<String, Object> variables = natTaskService.getVariables(task.getTaskId());
		User curUser = ContextUtil.getCurrentUser();

		List<PartyEntity> groups = new ArrayList<PartyEntity>();
		String data = entityService.findByUserAccountJson(curUser.getAccount());
		if (JacksonUtil.isJsonArray(data)) {
			List<PartyEntityPo> pes = JacksonUtil.getDTOList(data, PartyEntityPo.class);
			groups.addAll(pes);
		}

		List<String> gids = PartyUtil.partyToGroupId(groups);
		List<PrivilegeMode> privilege = signService.getPrivilege(curUser.getUserId(), gids,
				(SignNodeDefine) taskNodeDef, variables, dataObjects);
		// 拥有直接处理会签任务的特权
		if (privilege.contains(PrivilegeMode.ALL) || privilege.contains(PrivilegeMode.DIRECT)) {
			autoView.put("directHandlerSign", true);
		}
	}

	/**
	 * 处理同意时路径数据
	 *
	 * @param task
	 * @param defId
	 * @param autoView
	 * @param taskNodeDef
	 * @param jumpType
	 */
	private void handlePath(BpmTaskPo task, String defId, Map<String, Object> autoView, IBpmNodeDefine taskNodeDef,
			String jumpType) {
		List<IBpmNodeDefine> outcomeNodes = taskNodeDef.getOutgoingNodeList();
		List<IBpmNodeDefine> handlerSelectOutcomeNodes = handlerSelectOutgoingNodes(outcomeNodes);

		if (jumpType.contains("select")) {
			autoView.put("outgoingNodes", handlerSelectOutcomeNodes);

			// 计算人员配置结果
			calcUsers(task, autoView, handlerSelectOutcomeNodes, "outgoingNodesUsersMap");
		}

		if (jumpType.contains("free")) {
			List<IBpmNodeDefine> allNodeDef = bpmDefineReader.findNode(defId);
			// 移除开始节点
			List<IBpmNodeDefine> removeList = new ArrayList<IBpmNodeDefine>();
			for (IBpmNodeDefine bpmNodeDef : allNodeDef) {
				if (NodeType.START.equals(bpmNodeDef.getType())) {
					removeList.add(bpmNodeDef);
				}
			}
			allNodeDef.removeAll(removeList);
			autoView.put("allNodeDef", allNodeDef);

			// 计算人员配置结果
			calcUsers(task, autoView, allNodeDef, "allNodeDefUsersMap");
		}

		List<IBpmNodeDefine> pathOutgoingNodes = new ArrayList<>(handlerSelectOutcomeNodes);
		List<IBpmNodeDefine> removeList = new ArrayList<IBpmNodeDefine>();
		for (IBpmNodeDefine bpmNodeDef : pathOutgoingNodes) {
			if (NodeType.END.equals(bpmNodeDef.getType())) {
				removeList.add(bpmNodeDef);
			}
		}
		pathOutgoingNodes.removeAll(removeList);
		autoView.put("pathOutgoingNodes", pathOutgoingNodes);

		// 计算人员配置结果
		calcUsers(task, autoView, pathOutgoingNodes, "pathOutgoingNodesUsersMap");
	}

	/**
	 * 计算人员配置结果
	 *
	 * @param task
	 * @param autoView
	 * @param handlerSelectOutcomeNodes
	 */
	private void calcUsers(BpmTaskPo task, Map<String, Object> autoView, List<IBpmNodeDefine> nodeList, String key) {
		List<BpmIdentity> userList;
		List<Map<String, String>> userMapList;
		Map<String, List<Map<String, String>>> userListMap;
		if (BeanUtils.isNotEmpty(nodeList)) {
			userListMap = new HashMap<String, List<Map<String, String>>>();
			for (IBpmNodeDefine nodeDef : nodeList) {
				userList = bpmIdentityService.getByNode(task.getProcInstId(), nodeDef.getNodeId(), true);
				userMapList = bpmIdentityExtractService.extractUser(userList);
				userListMap.put(nodeDef.getNodeId(), userMapList);
			}
			autoView.put(key, JSONObject.fromObject(userListMap));
		}
	}

	/**
	 * 处理选择路径跳转的分支出口
	 *
	 * @param outgoingNodes
	 * @return
	 */
	private List<IBpmNodeDefine> handlerSelectOutgoingNodes(List<IBpmNodeDefine> outgoingNodes) {
		if (BeanUtils.isEmpty(outgoingNodes)) {
			return Collections.emptyList();
		}

		List<IBpmNodeDefine> returnList = new ArrayList<IBpmNodeDefine>();
		for (IBpmNodeDefine bpmNodeDef : outgoingNodes) {
			NodeType nodeType = bpmNodeDef.getType();
			// 网关节点
			if (NodeType.EXCLUSIVEGATEWAY.equals(nodeType) || NodeType.INCLUSIVEGATEWAY.equals(nodeType)
					|| NodeType.PARALLELGATEWAY.equals(nodeType)) {
				returnList.addAll(handlerSelectOutgoingNodes(bpmNodeDef.getOutgoingNodeList()));
			} else {
				returnList.add(bpmNodeDef);
			}
		}

		return returnList;
	}

}
